package com.caipiao.lottery.lty.server;

/**
 * @author GaoFeng
 */

import static org.jboss.netty.channel.Channels.pipeline;
import static org.jboss.netty.handler.codec.http.HttpHeaders.setContentLength;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static org.jboss.netty.handler.codec.http.HttpMethod.POST;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.WriteCompletionEvent;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.util.CharsetUtil;

import java.net.InetSocketAddress;   
import java.net.UnknownHostException;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import com.caipiao.lottery.log.TraceLogger;
import com.caipiao.lottery.lty.handler.LtyHandlerHelper;

//主监听服务
public class LtyHttpServer
{
	private int m_port;
	private final String m_context;
	private final String m_mode;
	
	public LtyHttpServer(String mode, int port, String context)
	{
		this.m_mode = mode;
		this.m_port = port;
		this.m_context = context;
	}
	
	ServerBootstrap bootstrap = null;
	
	public void startServer() throws UnknownHostException
	{
		Executor srvThrdPool = Executors.newCachedThreadPool();
		
		if (this.m_mode.equalsIgnoreCase("NIO"))
		{
			bootstrap = new ServerBootstrap(
	                new NioServerSocketChannelFactory(
	                		srvThrdPool,
	                		srvThrdPool));
		}
		else
		{
			bootstrap = new ServerBootstrap(
	                new OioServerSocketChannelFactory(
	                		srvThrdPool,
	                		srvThrdPool));
		}
		
		bootstrap.setPipelineFactory(new LtyHttpServerPipelineFactory());
		
		bootstrap.bind(new InetSocketAddress(this.m_port));
		
		//InetAddress addr = InetAddress.getLocalHost();
		//String ip=addr.getHostAddress().toString();//获得本机IP
		
		String ip = "localhost";
		
		String url = "http://" + ip + ":" + String.valueOf(this.m_port) + this.m_context;
		TraceLogger.System.debug("模式:" + this.m_mode + ", LtyHttpServer访问地址:  " + url + "  进行测试");
	}
	
	class LtyHttpServerPipelineFactory implements ChannelPipelineFactory
	{
	    public ChannelPipeline getPipeline() throws Exception 
	    {
	        ChannelPipeline pipeline = pipeline();
	        pipeline.addLast("decoder", new HttpRequestDecoder());
	        pipeline.addLast("aggregator", new HttpChunkAggregator(1024000));	//1000k数据
	        pipeline.addLast("encoder", new HttpResponseEncoder());
	        pipeline.addLast("handler", new LtyHttpServerHandler(m_context)); 
	        return pipeline;
	    }
	}
}

//HTTP处理句柄
class LtyHttpServerHandler extends SimpleChannelUpstreamHandler 
{

	private String RemoteIP = "";
	
	HandlerResult handler_result = new HandlerResult();
	
	/*
	long start_time = 0;
	StringBuffer log_buff = new StringBuffer();
	boolean b_log = false;
	long proc_diff = 0;
	*/
	
	private String m_context;
	public LtyHttpServerHandler(String context)
	{
		this.m_context = context;
	}
	
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)throws Exception
    {
    	handler_result.start_time = new Date().getTime();
    }
    
    @Override
    public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e)
    {
    	if (handler_result.b_log)
    	{
        	//耗时
    		long time_diff = new Date().getTime() - handler_result.start_time;
    		
    		handler_result.log_buff.append(" 响应时间:" + time_diff + "ms" + ctx.getAttachment().toString());
    		
    		if (handler_result.proc_diff > 1000 
    				|| (time_diff - handler_result.proc_diff) > 1000
    				|| !handler_result.handler_result)	//非正常处理
    		{
    			TraceLogger.Biz.warn(handler_result.log_buff.toString());
    		}
    		else
    		{
    			TraceLogger.Biz.info(handler_result.log_buff.toString());
    		}
    	}
    }
	
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception 
    {
        Object msg = e.getMessage();
        if (msg instanceof HttpRequest) 
        {
        	HttpRequest req = (HttpRequest) msg;
        	String req_uri = req.getUri();

        	//非指定上下文地址访问
        	if (!req_uri.startsWith(this.m_context))
        	{
        		HttpResponse resp = new DefaultHttpResponse(HTTP_1_1, FORBIDDEN);
            	ChannelFuture cf = ctx.getChannel().write(resp);
                cf.addListener(ChannelFutureListener.CLOSE);		//总是断开连接
                return;
        	}

        	InetSocketAddress isa = (InetSocketAddress)e.getRemoteAddress();
          	RemoteIP = isa.getAddress().getHostAddress();
          	
            String Resp_Str = "";

        	if (req.getMethod() == POST)
        	{
            	//处理有效请求
                ChannelBuffer req_buff = req.getContent();

                String Req_Str = req_buff.toString(CharsetUtil.UTF_8);

	            //交互日志
	            TraceLogger.LtyBin.info("LtySrv " + req_uri + "接收" + RemoteIP + "请求数据:\n" + Req_Str);

                //消息处理句柄
                LtyMsgHandler msg_handler = new LtyMsgHandler();

                String[] req_path = req_uri.split("[/]");
                if (req_path.length >= 6)
                {
                	String productId = req_path[2];
                	String channelId = req_path[3];
                	String cmd = req_path[4];
                	String version = req_path[5];
                	
                    long start_time = new Date().getTime();
                    
                    Resp_Str = msg_handler.Process(Req_Str, handler_result, productId, channelId, version, cmd);
                    
                  //耗时
                    handler_result.proc_diff = new Date().getTime() - start_time;
        			
                    handler_result.log_buff.append(" 处理时间:" + handler_result.proc_diff + "ms");
        			
        			ctx.setAttachment(" 输入:" + Req_Str.getBytes().length + "byte 输出:"+Resp_Str.getBytes().length+"byte");
    	            
        			//交互日志
    	            TraceLogger.LtyBin.info("LtySrv 发送" + RemoteIP +"响应数据:\n" + Resp_Str);
    	            
    	            if (Resp_Str != null && !Resp_Str.isEmpty())
    	            {
    	            	handler_result.b_log = true;
    	            }
                }
        	}
        	else		//测试
        	{
        		TraceLogger.Lty.trace("OTHER TYPE:" + msg.toString());
        		
        		Resp_Str = LtyHandlerHelper.GetTestHandleString(RemoteIP);
        	}
        	
            ChannelBuffer resp_buff =  ChannelBuffers.copiedBuffer(Resp_Str, CharsetUtil.UTF_8);
            
            HttpResponse resp = new DefaultHttpResponse(HTTP_1_1, OK);
            resp.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
            resp.setContent(resp_buff);
            setContentLength(resp, resp_buff.readableBytes());
            ChannelFuture cf = ctx.getChannel().write(resp);
            cf.addListener(ChannelFutureListener.CLOSE);		//总是断开连接
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception 
    {
        TraceLogger.Lty.error(e.toString(), e);
        e.getChannel().close();
    }
}


